{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Counterfactual with Reinforcement Learning (CFRL) on MNIST"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This method is described in [Model-agnostic and Scalable Counterfactual Explanations via Reinforcement Learning](https://arxiv.org/abs/2106.02597) and can generate counterfactual instances for any black-box model. The usual optimization procedure is transformed into a learnable process allowing to generate batches of counterfactual instances in a single forward pass even for high dimensional data. The training pipeline is model-agnostic and relies only on prediction feedback by querying the black-box model. Furthermore, the method allows target and feature conditioning. \n",
    "\n",
    "**We exemplify the use case for the TensorFlow backend. This means that all models: the autoencoder, the actor and the critic are TensorFlow models. Our implementation supports PyTorch backend as well.**\n",
    "\n",
    "CFRL uses [Deep Deterministic Policy Gradient (DDPG)](https://arxiv.org/abs/1509.02971) by interleaving a state-action function approximator called critic, with a learning an approximator called actor to predict the optimal action. The method assumes that the critic is differentiable with respect to the action argument, thus allowing to optimize the actor's parameters efficiently through gradient-based methods.\n",
    "\n",
    "The DDPG algorithm requires two separate networks, an actor $\\mu$ and a critic $Q$. Given the encoded representation of the input instance $z = enc(x)$, the model prediction $y_M$, the target prediction\n",
    "$y_T$ and the conditioning vector $c$, the actor outputs the counterfactual’s latent representation $z_{CF} = \\mu(z, y_M, y_T, c)$. The decoder then projects the embedding $z_{CF}$ back to the original input space,\n",
    "followed by optional post-processing.\n",
    "\n",
    "The training step consists of simultaneously optimizing the actor and critic networks. The critic regresses on the reward $R$ determined by the model prediction, while the actor maximizes the critic’s output for the given instance through $L_{max}$. The actor also minimizes two objectives to encourage the generation of sparse, in-distribution counterfactuals. The sparsity loss $L_{sparsity}$ operates on the decoded counterfactual $x_{CF}$ and combines the $L_1$ loss over the standardized numerical features and the $L_0$ loss over the categorical ones. The consistency loss $L_{consist}$ aims to encode the counterfactual $x_{CF}$ back to the same latent representation where it was decoded from and helps to produce in-distribution counterfactual instances.Formally, the actor's loss can be written as:\n",
    "$L_{actor} = L_{max} + \\lambda_{1}L_{sparsity} + \\lambda_{2}L_{consistency}$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "Note\n",
    "    \n",
    "To enable support for CounterfactualRLTabular with tensorflow backend, you may need to run\n",
    "    \n",
    "```bash\n",
    "pip install alibi[tensorflow]\n",
    "```\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from typing import Dict\n",
    "\n",
    "import os\n",
    "os.environ[\"TF_USE_LEGACY_KERAS\"] = \"1\"\n",
    "\n",
    "import tensorflow as tf\n",
    "import tensorflow.keras as keras\n",
    "\n",
    "from alibi.explainers import CounterfactualRL\n",
    "from alibi.models.tensorflow import AE\n",
    "from alibi.models.tensorflow import Actor, Critic\n",
    "from alibi.models.tensorflow import MNISTEncoder, MNISTDecoder, MNISTClassifier\n",
    "from alibi.explainers.cfrl_base import Callback"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load MNIST dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define constants.\n",
    "BATCH_SIZE = 64\n",
    "BUFFER_SIZE = 1024\n",
    "\n",
    "# Load MNIST dataset.\n",
    "(X_train, Y_train), (X_test, Y_test) = tf.keras.datasets.mnist.load_data()\n",
    "\n",
    "# Expand dimensions and normalize.\n",
    "X_train = np.expand_dims(X_train, axis=-1).astype(np.float32) / 255.\n",
    "X_test = np.expand_dims(X_test, axis=-1).astype(np.float32) / 255.\n",
    "\n",
    "# Define trainset.\n",
    "trainset_classifier = tf.data.Dataset.from_tensor_slices((X_train, Y_train))\n",
    "trainset_classifier = trainset_classifier.shuffle(buffer_size=BUFFER_SIZE).batch(BATCH_SIZE)\n",
    "\n",
    "# Define testset.\n",
    "testset_classifier = tf.data.Dataset.from_tensor_slices((X_test, Y_test))\n",
    "testset_classifier = testset_classifier.shuffle(buffer_size=BUFFER_SIZE).batch(BATCH_SIZE)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define and train CNN classifier"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/5\n",
      "938/938 [==============================] - 12s 11ms/step - loss: 0.4742 - sparse_categorical_accuracy: 0.8550\n",
      "Epoch 2/5\n",
      "938/938 [==============================] - 12s 13ms/step - loss: 0.0935 - sparse_categorical_accuracy: 0.9709\n",
      "Epoch 3/5\n",
      "938/938 [==============================] - 11s 12ms/step - loss: 0.0639 - sparse_categorical_accuracy: 0.9799\n",
      "Epoch 4/5\n",
      "938/938 [==============================] - 11s 12ms/step - loss: 0.0516 - sparse_categorical_accuracy: 0.9832\n",
      "Epoch 5/5\n",
      "938/938 [==============================] - 11s 12ms/step - loss: 0.0453 - sparse_categorical_accuracy: 0.9851\n",
      "INFO:tensorflow:Assets written to: tensorflow/MNIST_classifier/assets\n"
     ]
    }
   ],
   "source": [
    "# Number of classes.\n",
    "NUM_CLASSES = 10\n",
    "EPOCHS = 5\n",
    "\n",
    "# Define classifier path and create dir if it doesn't exist.\n",
    "classifier_path = os.path.join(\"tensorflow\", \"MNIST_classifier\")\n",
    "if not os.path.exists(classifier_path):\n",
    "    os.makedirs(classifier_path)\n",
    "\n",
    "# Construct classifier. This is the classifier used in the paper experiments.\n",
    "classifier = MNISTClassifier(output_dim=NUM_CLASSES)\n",
    "\n",
    "# Define optimizer and loss function\n",
    "optimizer = keras.optimizers.Adam(learning_rate=1e-3)\n",
    "loss = keras.losses.SparseCategoricalCrossentropy(from_logits=True)\n",
    "\n",
    "# Complile the model.\n",
    "classifier.compile(optimizer=optimizer, \n",
    "                   loss=loss,\n",
    "                   metrics=[tf.keras.metrics.SparseCategoricalAccuracy()])\n",
    "\n",
    "\n",
    "if len(os.listdir(classifier_path)) == 0:\n",
    "    # Fit and save the classifier.\n",
    "    classifier.fit(trainset_classifier, epochs=EPOCHS)\n",
    "    classifier.save(classifier_path)\n",
    "else:\n",
    "    # Load the classifier if already fitted.\n",
    "    classifier = keras.models.load_model(classifier_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "157/157 [==============================] - 1s 6ms/step - loss: 0.0383 - sparse_categorical_accuracy: 0.9879\n"
     ]
    }
   ],
   "source": [
    "# Evaluate the classifier\n",
    "loss, accuracy = classifier.evaluate(testset_classifier)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define the predictor (black-box)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we've trained the CNN classifier, we can define the black-box model. Note that the output of the black-box is a distribution which can be either a soft-label distribution (probabilities/logits for each class) or a hard-label distribution (one-hot encoding). Internally, CFRL takes the `argmax`. Moreover the output **DOES NOT HAVE TO BE DIFFERENTIABLE**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define predictor function (black-box) used to train the CFRL\n",
    "def predictor(X: np.ndarray):\n",
    "    Y = classifier(X).numpy()\n",
    "    return Y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define and train autoencoder"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Instead of directly modeling the perturbation vector in the potentially high-dimensional input space, we first train an autoencoder. The weights of the encoder are frozen and the actor applies the\n",
    "counterfactual perturbations in the latent space of the encoder. The pre-trained decoder maps the counterfactual embedding back to the input feature space. \n",
    "\n",
    "The autoencoder follows a standard design. The model is composed from two submodules, the encoder and the decoder. The forward pass consists of passing the input to the encoder, obtain the input embedding and pass the embedding through the decoder.\n",
    "\n",
    "```python\n",
    "class AE(keras.Model):\n",
    "    def __init__(self, encoder: keras.Model, decoder: keras.Model, **kwargs) -> None:\n",
    "        super().__init__(**kwargs)\n",
    "        self.encoder = encoder\n",
    "        self.decoder = decoder\n",
    "\n",
    "    def call(self, x: tf.Tensor, **kwargs):\n",
    "        z = self.encoder(x)\n",
    "        x_hat = self.decoder(z)\n",
    "        return x_hat\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define autoencoder trainset.\n",
    "trainset_ae = tf.data.Dataset.from_tensor_slices(X_train)\n",
    "trainset_ae = trainset_ae.map(lambda x: (x, x))\n",
    "trainset_ae = trainset_ae.shuffle(buffer_size=BUFFER_SIZE).batch(BATCH_SIZE)\n",
    "\n",
    "# Define autoencode testset.\n",
    "testset_ae = tf.data.Dataset.from_tensor_slices(X_test)\n",
    "testset_ae = testset_ae.map(lambda x: (x, x))\n",
    "testset_ae = testset_ae.shuffle(buffer_size=BUFFER_SIZE).batch(BATCH_SIZE)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/50\n",
      "938/938 [==============================] - 9s 8ms/step - loss: 0.2846\n",
      "Epoch 2/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1431\n",
      "Epoch 3/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1287\n",
      "Epoch 4/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1224\n",
      "Epoch 5/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1184\n",
      "Epoch 6/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1155\n",
      "Epoch 7/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1129\n",
      "Epoch 8/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1111\n",
      "Epoch 9/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1094\n",
      "Epoch 10/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1078\n",
      "Epoch 11/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1066\n",
      "Epoch 12/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1056\n",
      "Epoch 13/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1045\n",
      "Epoch 14/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1038\n",
      "Epoch 15/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1030\n",
      "Epoch 16/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1023\n",
      "Epoch 17/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1016\n",
      "Epoch 18/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1010\n",
      "Epoch 19/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1005\n",
      "Epoch 20/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.1000\n",
      "Epoch 21/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.0997\n",
      "Epoch 22/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.0992\n",
      "Epoch 23/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.0987\n",
      "Epoch 24/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.0983\n",
      "Epoch 25/50\n",
      "938/938 [==============================] - 7s 7ms/step - loss: 0.0980\n",
      "Epoch 26/50\n",
      "938/938 [==============================] - 7s 8ms/step - loss: 0.0977\n",
      "Epoch 27/50\n",
      "938/938 [==============================] - 7s 8ms/step - loss: 0.0973\n",
      "Epoch 28/50\n",
      "938/938 [==============================] - 7s 7ms/step - loss: 0.0971\n",
      "Epoch 29/50\n",
      "938/938 [==============================] - 7s 7ms/step - loss: 0.0967\n",
      "Epoch 30/50\n",
      "938/938 [==============================] - 7s 7ms/step - loss: 0.0964\n",
      "Epoch 31/50\n",
      "938/938 [==============================] - 7s 7ms/step - loss: 0.0962\n",
      "Epoch 32/50\n",
      "938/938 [==============================] - 7s 7ms/step - loss: 0.0960\n",
      "Epoch 33/50\n",
      "938/938 [==============================] - 7s 8ms/step - loss: 0.0957\n",
      "Epoch 34/50\n",
      "938/938 [==============================] - 8s 9ms/step - loss: 0.0954\n",
      "Epoch 35/50\n",
      "938/938 [==============================] - 7s 8ms/step - loss: 0.0953\n",
      "Epoch 36/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.0951\n",
      "Epoch 37/50\n",
      "938/938 [==============================] - 7s 8ms/step - loss: 0.0949\n",
      "Epoch 38/50\n",
      "938/938 [==============================] - 7s 8ms/step - loss: 0.0948\n",
      "Epoch 39/50\n",
      "938/938 [==============================] - 7s 8ms/step - loss: 0.0944\n",
      "Epoch 40/50\n",
      "938/938 [==============================] - 9s 10ms/step - loss: 0.0941\n",
      "Epoch 41/50\n",
      "938/938 [==============================] - 7s 7ms/step - loss: 0.0940\n",
      "Epoch 42/50\n",
      "938/938 [==============================] - 8s 9ms/step - loss: 0.0938\n",
      "Epoch 43/50\n",
      "938/938 [==============================] - 9s 10ms/step - loss: 0.0937\n",
      "Epoch 44/50\n",
      "938/938 [==============================] - 7s 7ms/step - loss: 0.0935\n",
      "Epoch 45/50\n",
      "938/938 [==============================] - 7s 8ms/step - loss: 0.0933\n",
      "Epoch 46/50\n",
      "938/938 [==============================] - 7s 7ms/step - loss: 0.0932\n",
      "Epoch 47/50\n",
      "938/938 [==============================] - 7s 7ms/step - loss: 0.0930\n",
      "Epoch 48/50\n",
      "938/938 [==============================] - 7s 7ms/step - loss: 0.0929\n",
      "Epoch 49/50\n",
      "938/938 [==============================] - 8s 8ms/step - loss: 0.0928\n",
      "Epoch 50/50\n",
      "938/938 [==============================] - 7s 8ms/step - loss: 0.0925\n",
      "INFO:tensorflow:Assets written to: tensorflow/MNIST_autoencoder/assets\n"
     ]
    }
   ],
   "source": [
    "# Define autoencoder path and create dir if it doesn't exist.\n",
    "ae_path = os.path.join(\"tensorflow\", \"MNIST_autoencoder\")\n",
    "if not os.path.exists(ae_path):\n",
    "    os.makedirs(ae_path)\n",
    "\n",
    "# Define latent dimension.\n",
    "LATENT_DIM = 64\n",
    "EPOCHS = 50\n",
    "    \n",
    "# Define autoencoder.\n",
    "ae = AE(encoder=MNISTEncoder(latent_dim=LATENT_DIM),\n",
    "        decoder=MNISTDecoder())\n",
    "\n",
    "# Define optimizer and loss function.\n",
    "optimizer = keras.optimizers.Adam(learning_rate=1e-3)\n",
    "loss = keras.losses.BinaryCrossentropy(from_logits=False)\n",
    "\n",
    "# Compile autoencoder.\n",
    "ae.compile(optimizer=optimizer, loss=loss)\n",
    "\n",
    "if len(os.listdir(ae_path)) == 0:\n",
    "    # Fit and save autoencoder.\n",
    "    ae.fit(trainset_ae, epochs=EPOCHS)\n",
    "    ae.save(ae_path)\n",
    "else:\n",
    "    # Load the model.\n",
    "    ae = keras.models.load_model(ae_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Test the autoencoder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define number of samples to be displayed\n",
    "NUM_SAMPLES = 5\n",
    "\n",
    "# Get some random samples from test\n",
    "np.random.seed(0)\n",
    "indices = np.random.choice(X_test.shape[0], NUM_SAMPLES)\n",
    "inputs = [X_test[i].reshape(1, 28, 28, 1) for i in indices]\n",
    "inputs = np.concatenate(inputs, axis=0)\n",
    "\n",
    "# Pass samples through the autoencoder\n",
    "inputs_hat = ae(inputs).numpy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 1800x720 with 10 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot inputs and reconstructions.\n",
    "plt.rcParams.update({'font.size': 22})\n",
    "fig, ax = plt.subplots(2, NUM_SAMPLES, figsize=(25, 10))\n",
    "\n",
    "for i in range(NUM_SAMPLES):\n",
    "    ax[0][i].imshow(inputs[i], cmap='gray')\n",
    "    ax[1][i].imshow(inputs_hat[i], cmap='gray')\n",
    "    \n",
    "text1 = ax[0][0].set_ylabel(\"x\")\n",
    "text2 = ax[1][0].set_ylabel(\"x_hat\")        "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Counterfactual with Reinforcement Learning"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define constants\n",
    "COEFF_SPARSITY = 7.5               # sparisty coefficient\n",
    "COEFF_CONSISTENCY = 0              # consisteny coefficient -> no consistency\n",
    "TRAIN_STEPS = 50000                # number of training steps -> consider increasing the number of steps\n",
    "BATCH_SIZE = 100                   # batch size"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Define and fit the explainer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define explainer.\n",
    "explainer = CounterfactualRL(predictor=predictor,\n",
    "                             encoder=ae.encoder,\n",
    "                             decoder=ae.decoder,\n",
    "                             latent_dim=LATENT_DIM,\n",
    "                             coeff_sparsity=COEFF_SPARSITY,\n",
    "                             coeff_consistency=COEFF_CONSISTENCY,\n",
    "                             train_steps=TRAIN_STEPS,\n",
    "                             batch_size=BATCH_SIZE,\n",
    "                             backend=\"tensorflow\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 50000/50000 [31:04<00:00, 26.82it/s]\n"
     ]
    }
   ],
   "source": [
    "# Fit the explainer\n",
    "explainer = explainer.fit(X=X_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Test explainer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2/2 [00:00<00:00, 36.14it/s]\n"
     ]
    }
   ],
   "source": [
    "# Generate counterfactuals for some test instances.\n",
    "explanation = explainer.explain(X_test[0:200], Y_t=np.array([2]), batch_size=100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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Oc95TrPmS1/6Uuf4rbfe2pwD+tTKh8997nPNOV784hd3jq9fv745x2WXXdasqbV2jl933XXNOZ73vOd1jj/5yU9uznHGGWc0a9as6f5fd2vPkvafF5iM3XbbrVlzyimndI6fd955zTm+8IUvdI6Pk2Ef/vCHmzWPecxjOsfvd7/7Nef4zne+06wZ5/klLGfjvN5btar7/WlPecpTmnO84x3vmMhaFsM4z022bNnSOb7zzjs35/j5n//5Zs25557bOb5p06bmHLDc3f/+9+8c33///ZtzbNiwoVnzL//yL53j/9//9/8157jooos6x2+77bbmHONkYau3dcQRRzTn2GuvvZo1F198cef4v/3bvzXn0C+a4p3eAAAAAAAMhqY3AAAAAACDoekNAAAAAMBgaHoDAAAAADAYmt4AAAAAAAyGpjcAAAAAAIOh6Q0AAAAAwGCs6XsB/LRaa7Nm06ZN836c9evXN2ve/OY3d47/4Ac/aM5xzDHHNGsOOOCAzvELL7ywOccd73jHzvEddtihOceOO+7YrFmzpvufTevPkiS/8zu/06z56le/2jn+r//6r805PvShD3WOb968uTkHLJRx/v61/h2sWtX+2e2WLVvGXtN8tHL5qquuas7xla98pVnzvve9r3P8rLPOas4BLI5bbrmlWfOJT3yic/yHP/xhc47bbrutc3yc55bXXXdds+amm27qHB8n59auXdusafH8hT6N89xjt9126xw/9NBDm3McfPDBneMvfOELm3OMs9ZWfpRSmnO03Hjjjc2aa6+9tllzww03dI7/+7//e3OOE044oVnz0Ic+tHP83e9+d3OOSy+9tHN8nFyGhbL77rs3a+5973t3jp955pnNOT71qU81a77xjW90jreeeyST+fc0Tta1niu18jRJDj/88GbNySef3Dl+3nnnNed405ve1Dk+zlqHwDu9AQAAAAAYDE1vAAAAAAAGQ9MbAAAAAIDB0PQGAAAAAGAwNL0BAAAAABiMJdX0LqXcq5TyklLK+0spF5dStpRSainlxDHu+/RSynmllOtLKRtLKeeXUk4tpSypPyOwNMkfoE8yCOiL/AH6JIOAhbKm7wVM84IkL5ntnUopb0vyG0luSXJOkk1Jjk3y1iTHllJOqrVunuRCgcGRP0CfZBDQF/kD9EkGAQtiqf3068Ikf5Lk5CSHJvls6w6l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",
      "text/plain": [
       "<Figure size 1800x720 with 10 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots(2, NUM_SAMPLES, figsize=(25, 10))\n",
    "\n",
    "for i in range(NUM_SAMPLES):\n",
    "    ax[0][i].imshow(explanation.data['orig']['X'][i], cmap='gray')\n",
    "    ax[1][i].imshow(explanation.data['cf']['X'][i], cmap='gray')\n",
    "    \n",
    "    ax[0][i].set_xlabel(\"Label: \" + str(explanation.data['orig']['class'][i]))\n",
    "    ax[1][i].set_xlabel(\"Label: \" + str(explanation.data['cf']['class'][i]))\n",
    "    \n",
    "\n",
    "text1 = ax[0][0].set_ylabel(\"X\")\n",
    "text2 = ax[1][0].set_ylabel(\"X_hat\")    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Logging"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Logging is clearly important when dealing with deep learning models. Thus, we provide an interface to write custom callbacks for logging purposes after each training step which we defined [here](../api/alibi.explainers.cfrl_base.rst#alibi.explainers.cfrl_base.Callback). In the following cells we provide some example to log in **Weights and Biases**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Logging reward callback"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "class RewardCallback(Callback):\n",
    "    def __call__(self,\n",
    "                 step: int, \n",
    "                 update: int, \n",
    "                 model: CounterfactualRL,\n",
    "                 sample: Dict[str, np.ndarray],\n",
    "                 losses: Dict[str, float]):\n",
    "        if step % 100 != 0:\n",
    "            return\n",
    "        \n",
    "        # Get the counterfactual and target.\n",
    "        X_cf = sample[\"X_cf\"]\n",
    "        Y_t = sample[\"Y_t\"]\n",
    "        \n",
    "        # Get prediction label.\n",
    "        Y_m_cf = predictor(X_cf)\n",
    "        \n",
    "        # Compute reward\n",
    "        reward = np.mean(model.params[\"reward_func\"](Y_m_cf, Y_t))\n",
    "        wandb.log({\"reward\": reward})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Logging images callback"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ImagesCallback(Callback):\n",
    "    def __call__(self,\n",
    "                 step: int, \n",
    "                 update: int, \n",
    "                 model: CounterfactualRL,\n",
    "                 sample: Dict[str, np.ndarray],\n",
    "                 losses: Dict[str, float]):\n",
    "        # Log every 100 steps\n",
    "        if step % 100 != 0:\n",
    "            return\n",
    "        \n",
    "        # Defie number of samples to be displayed.\n",
    "        NUM_SAMPLES = 10\n",
    "        \n",
    "        X = sample[\"X\"][:NUM_SAMPLES]        # input instance\n",
    "        X_cf = sample[\"X_cf\"][:NUM_SAMPLES]  # counterfactual\n",
    "        diff = np.abs(X - X_cf)              # differences\n",
    "        \n",
    "        Y_m = sample[\"Y_m\"][:NUM_SAMPLES].astype(int)   # input labels\n",
    "        Y_t = sample[\"Y_t\"][:NUM_SAMPLES].astype(int)   # target labels\n",
    "        Y_m_cf = predictor(X_cf).astype(int)            # counterfactual labels\n",
    "        \n",
    "        # Concatentate images,\n",
    "        X = np.concatenate(X, axis=1)\n",
    "        X_cf = np.concatenate(X_cf, axis=1)\n",
    "        diff = np.concatenate(diff, axis=1)\n",
    "        \n",
    "        # Construct full image.\n",
    "        img = np.concatenate([X, X_cf, diff], axis=0)\n",
    "            \n",
    "        # Construct caption.\n",
    "        caption = \"\"\n",
    "        caption += \"Input:\\t%s\\n\" % str(list(np.argmax(Y_m, axis=1)))\n",
    "        caption += \"Target:\\t%s\\n\" % str(list(np.argmax(Y_t, axis=1)))\n",
    "        caption += \"Predicted:\\t%s\\n\" % str(list(np.argmax(Y_m_cf, axis=1)))\n",
    "        \n",
    "        # Log image.\n",
    "        wandb.log({\"samples\": wandb.Image(img, caption=caption)})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Logging losses callback"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "class LossCallback(Callback):\n",
    "    def __call__(self,\n",
    "                 step: int, \n",
    "                 update: int, \n",
    "                 model: CounterfactualRL,\n",
    "                 sample: Dict[str, np.ndarray],\n",
    "                 losses: Dict[str, float]):\n",
    "        # Log evary 100 updates.\n",
    "        if (step + update) % 100 == 0:\n",
    "            wandb.log(losses)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Having defined the callbacks, we can define a new explainer that will include logging.\n",
    "\n",
    "```python\n",
    "import wandb\n",
    "\n",
    "# Initialize wandb.\n",
    "wandb_project = \"MNIST Counterfactual with Reinforcement Learning\"\n",
    "wandb.init(project=wandb_project)\n",
    "\n",
    "# Define explainer as before and include callbacks.\n",
    "explainer = CounterfactualRL(...,\n",
    "                             callbacks=[RewardCallback(), ImagesCallback()])\n",
    "\n",
    "# Fit the explainer.\n",
    "explainer.fit(X=X_train)\n",
    "\n",
    "# Close wandb.\n",
    "wandb.finish()\n",
    "```"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
